package org.netty.mvc.tool;

import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.CharsetUtil;
import org.netty.mvc.annotation.*;
import org.netty.mvc.entity.BeanMethod;
import org.netty.mvc.http.NettyApplication;
import org.netty.mvc.spring.BeanDefinition;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author Jun jie Yi
 * @Date 2022/3/7 22:01
 */
public class ResponseUtil {

    public static Map<String, Object> singletonObjects = NettyApplication.getApplicationContext().getSingletonObjects();

    public static Map<String, BeanDefinition> beanDefinitionMap = NettyApplication.getApplicationContext().getBeanDefinitionMap();

    public static Map<String, BeanMethod> cacheMethod = new ConcurrentHashMap<String, BeanMethod>();

    //注解容器，存储加了注解的类
    public static Object parseRequest(FullHttpRequest req) {
        //查询缓存记录，第二次请求的时候直接访问
        BeanMethod beanMethod = cacheMethod.get(URLTool.parseUrlKey(req.uri()));
        if (beanMethod != null) {
            return executeMethod(req, beanMethod.getBeanName(), beanMethod.getMethod());
        }
        //返回值
        Object result = null;
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            Class<?> clazz = beanDefinitionEntry.getValue().getClazz();
            //必须是加了NettyController的注解类
            if (clazz.isAnnotationPresent(NettyController.class)) {
                String s1 = "";
                if (clazz.isAnnotationPresent(NettyRequestMapping.class)) {
                    NettyRequestMapping anNoAnnotation = clazz.getAnnotation(NettyRequestMapping.class);
                    s1 = anNoAnnotation.value();
                }

                Method[] methods = clazz.getDeclaredMethods();
                for (int i = 0; i < methods.length; i++) {
                    //加了注解的方法
                    if (methods[i].isAnnotationPresent(NettyRequestMapping.class)) {
                        //通过getAnnotation可以获取注解对象
                        NettyRequestMapping annotation = methods[i].getAnnotation(NettyRequestMapping.class);
                        //地址
                        String s2 = annotation.value();
                        if (!"".equals(s1) && s1.charAt(0) != '/') {
                            s1 = "/" + s1;
                        }
                        if (!"".equals(s2) && s2.charAt(0) != '/') {
                            s2 = "/" + s2;
                        }
                        String url = s1 + s2;
                        //类型
                        RequestMethod type = annotation.method();
                        //是否有地址匹配上
                        if (req.uri().split("\\?")[0].equals(url)) {
                            if ((type.equals(RequestMethod.GET) && req.method() == HttpMethod.GET) || (type.equals(RequestMethod.POST) && req.method() == HttpMethod.POST)) {
                                result = executeMethod(req, beanName, methods[i]);
                            } else {
                                result = ErrorMessage.requestTypeError();
                            }
                            break;
                        }
                    }
                    if (i == methods.length - 1) {
                        result = ErrorMessage.notFoundError();
                    }
                }
            }
        }
        return result;
    }

    /**
     * 支持解析 @RequestParam， @RequestBody(这里只支持传入JsonObject)， HttpRequest类型自动匹配
     *
     * @param req
     * @param beanName
     * @param method
     * @return
     */
    private static Object executeMethod(FullHttpRequest req, String beanName, Method method) {
        //定义返回对象
        Object data = null;
        //禁止say方法的访问控制检查，private方法也可以执行
        method.setAccessible(true);
        Class<?>[] parameterTypes = method.getParameterTypes();
        //获取每个参数的注解，可能是一个也可能是多个
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        Object[] params = new Object[parameterTypes.length];
        //解析req在params中添加参数
        for (int i = 0; i < parameterTypes.length; i++) {
            //当前参数类型
            Class<?> parameterType = parameterTypes[i];
            //当前固定每个参数注解固定只有一个
            Annotation[] parameterAnnotation = parameterAnnotations[i];
            if (parameterAnnotation.length == 1) {//注解匹配的，这里参数只允许有一个注解
                Annotation annotation = parameterAnnotation[0];
                if (annotation instanceof RequestBody) {
                    params[i] = convertRequestBody(req);
                } else if (annotation instanceof RequestParam) {//此参数需要到req中解析
                    Map<String, String> stringStringMap = URLTool.parseQuery(req.uri().split("\\?")[1]);
                    String key = ((RequestParam) annotation).value();
                    String value = stringStringMap.get(key);
                    if (value == null) {
                        throw new RuntimeException("没有匹配的参数类型！");
                    } else {
                        if (parameterType.equals(int.class) || parameterType.equals(Integer.class)) {
                            params[i] = Integer.parseInt(value);
                        } else if (parameterType.equals(String.class)) {
                            params[i] = value;
                        }
                    }
                }
            } else if (parameterAnnotation.length == 0) {//类型匹配的
                //类型为HttpRequest
                if (parameterType.equals(HttpRequest.class)) {
                    params[i] = req;
                }
            } else {
                throw new RuntimeException("参数多个注解错误！");
            }
        }
        try {
            //获取执行后的返回值
            data = method.invoke(singletonObjects.get(beanName), params);
            //执行成功后添加缓存
            cacheMethod.put(URLTool.parseUrlKey(req.uri()), new BeanMethod(beanName, method));
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return data;
    }

    /**
     * 获取请求的内容
     *
     * @param req
     * @return
     */
    private static JSONObject convertRequestBody(FullHttpRequest req) {
        ByteBuf jsonBuf = req.content();
        String jsonStr = jsonBuf.toString(CharsetUtil.UTF_8);
        //将json字符串转成JSONObject对象
        return JSONObject.parseObject(jsonStr);
    }
}
